#include "Win32Window.h"

bool CWin32Window::CreateWnd( int width, int height, const TCHAR *title )
{
	WNDCLASSEX	windowClass;
	
	memset( &windowClass, 0, sizeof(WNDCLASSEX) );
	windowClass.cbSize			= sizeof(WNDCLASSEX);
	windowClass.style			= CS_HREDRAW | CS_VREDRAW;
	windowClass.lpfnWndProc		= MessageRouter;
	windowClass.cbClsExtra		= 0;
	windowClass.cbWndExtra		= 0;
	windowClass.hInstance		= GetModuleHandle( NULL );
	windowClass.hIcon			= LoadIcon( NULL, IDI_APPLICATION );
	windowClass.hCursor			= LoadCursor( NULL, IDC_ARROW );
	windowClass.hbrBackground	= NULL;
	windowClass.lpszMenuName	= NULL;
	windowClass.lpszClassName	= title;
	windowClass.hIconSm			= LoadIcon( NULL, IDI_WINLOGO );
	
	if ( !RegisterClassEx( &windowClass ) )
		return false;
	
	HWND hWnd = CreateWindowEx( NULL,
								title, 
								title, 
								WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 
								CW_USEDEFAULT, CW_USEDEFAULT, width, height, 
								NULL, NULL, 
								windowClass.hInstance, 
								this );
								
	if( NULL == hWnd )
		return false;
		
	return true;
}

void CWin32Window::MessageLoop()
{
	ShowWindow( m_hWnd, SW_SHOW );
	UpdateWindow( m_hWnd );

	MSG msg;
	long lCount = 0;
	bool bQuit = false;

	while( !bQuit )
	{
		if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
		{
			if ( msg.message != WM_QUIT )
			{
				TranslateMessage( &msg );
				DispatchMessage( &msg );
				lCount = 0;
			}
			else
				bQuit = true;
		}
		else
			OnIdle( ++lCount );
	}
}

LRESULT CALLBACK CWin32Window::MessageRouter( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	CWin32Window* pThis = NULL;
	if ( WM_CREATE == message )
	{
		reinterpret_cast<CWin32Window*>((LPCREATESTRUCT(lParam))->lpCreateParams)->m_hWnd = hWnd;
		SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>((LPCREATESTRUCT(lParam))->lpCreateParams) );
	}

	pThis = reinterpret_cast<CWin32Window*>(GetWindowLongPtr( hWnd, GWLP_USERDATA ));
	
	if ( NULL != pThis )
		return pThis->WndProc( message, wParam, lParam );
	else
		return DefWindowProc( hWnd, message, wParam, lParam );
}

int CWin32Window::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
	PreCreateWindow( *lpCreateStruct );
	
	SetWindowLong( m_hWnd, GWL_STYLE, lpCreateStruct->style );
	SetWindowLong( m_hWnd, GWL_EXSTYLE, lpCreateStruct->dwExStyle );
	
	return 0;
}

LRESULT CALLBACK CWin32Window::WndProc( UINT message, WPARAM wParam, LPARAM lParam )
{
	switch ( message )
	{
		case WM_CREATE:
			if( OnCreate( LPCREATESTRUCT(lParam) ) != 0 )
				PostQuitMessage( 0 );
			break;
		case WM_CLOSE:
			OnClose();
			break;
		case WM_DESTROY:
			OnDestroy();
			break;
		case WM_SIZE:
			OnSize( wParam, LOWORD( lParam ), HIWORD( lParam ) );
			break;
		case WM_PAINT:
			OnPaint();
			break;
		case WM_TIMER:
			OnTimer( (UINT)wParam );
			break;
		case WM_LBUTTONUP:
			OnLButtonUp( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_LBUTTONDOWN:
			OnLButtonDown( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_LBUTTONDBLCLK:
			OnLButtonDblClk( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_RBUTTONUP:
			OnRButtonUp( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_RBUTTONDOWN:
			OnRButtonDown( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_RBUTTONDBLCLK:
			OnRButtonDblClk( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_MBUTTONUP:
			OnMButtonUp( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_MBUTTONDOWN:
			OnMButtonDown( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_MBUTTONDBLCLK:
			OnMButtonDblClk( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_MOUSEMOVE:
			OnMouseMove( (UINT)wParam, (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_MOUSEWHEEL:
			OnMouseWheel( (UINT)LOWORD(wParam), (short)HIWORD(wParam), (int)LOWORD(lParam), (int)HIWORD(lParam) );
			break;
		case WM_COMMAND:
			OnCommand( wParam, lParam );
			break;
		case WM_KEYDOWN:
			OnKeyDown( (UINT)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam) );
			break;
		case WM_KEYUP:
			OnKeyUp( (UINT)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam) );
			break;
		case WM_CHAR:
			OnChar( (UINT)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam) );
			break;
		default:
			break;
	}

	return DefWindowProc( m_hWnd, message, wParam, lParam );
}